<template>
  <g :transform="`rotate(${rotate},${points[1].x},${points[3].y})`" @contextmenu="contextmenu($event, $el)">
    <Component
      :is="component"
      :points="points"
      :class="[isEditor ? 'vc-cursor-move' : 'vc-cursor-pointer']"
      :style="{ opacity }"
      :fill="fill"
    />
    <EditableText
      v-model:value="textValue"
      v-model:width="textWidth"
      v-model:height="textHeight"
      v-model:editable="textEditable"
      :x="textX"
      :y="textY"
    />
    <!-- 占位 -->
    <rect
      :x="x"
      :y="y"
      :width="width"
      :height="height"
      fill="rgba(0,0,0,0)"
      stroke="rgba(0,0,0,0)"
      @mousedown="mousedown"
      @mouseover="mouseover"
      @mousemove="mousemove"
      @mouseout="mouseout"
      @mouseup="mouseup"
      @dblclick="setText"
    />
    <!-- 高亮连接点 -->
    <circle
      v-if="curConnectPoint"
      :cx="curConnectPoint.x"
      :cy="curConnectPoint.y"
      r="6"
      fill="#4F80FF"
      stroke="rgba(255,255,255,0.8)"
      stroke-width="2"
    />
    <template v-if="selected">
      <rect
        :x="points[0].x"
        :y="points[0].y"
        :width="width"
        :height="height"
        fill="none"
        stroke="#4F80FF"
        stroke-dasharray="3 3"
        :style="{ 'stroke-width': isEditor ? 1 : 2 }"
        pointer-events="none"
      />
      <template v-if="isEditor">
        <ResizeHandle
          v-for="(el, i) in points"
          :key="el.x + '' + el.y + i"
          :center-x="el.x"
          :center-y="el.y"
          :cursor="operCursors[i]"
          @mousedown="rectDragStart ? rectDragStart($event, i) : undefined"
        />
        <ConfigureIcon
          :x="points[1].x - IconSize / 2"
          :y="points[1].y - FlowPathOperSize - 5 - IconSize / 2"
          :size="IconSize"
          @mousedown="rotateStart"
        />
      </template>
    </template>
  </g>
</template>
<script setup lang="ts">
import { computed, CSSProperties, defineAsyncComponent, inject, onBeforeUnmount, Ref, ref, watch } from "vue";
import {
  CompCommon,
  CompCommonKey,
  FlowPathType,
  Point,
  ConfigurationInstance,
  FlowPathJSON,
  DragMoveOptions,
} from "/@/components/types";
import Arrow from "./resource/Arrow.vue";
import { FlowPathOperSize, IconSize } from "/@/components/common";
import { DragStart, useDrag } from "/@/hooks/useDrag";
import { ConfigurationType, ContextmenuItem, SvgInfo } from "/@/Configuration/types";
import ConfigureIcon from "/@/components/base/ConfigureIcon.vue";
import ResizeHandle from "/@/components/base/ResizeHandle.vue";
import { useRotate } from "/@/hooks/useRotate";
import { useRectDrag } from "./useRectDrag";
import { calc2pointDistance, getRotatePoint, isRectIntersect } from "/@/utils/draw";
import { FlowPathPanelInfo } from "/@/Configuration/AttrPanel/types";
import { useConnEffect } from "/@/hooks/useConnEffect";
import EditableText from "/@/components/base/EditableText.vue";

const props = defineProps<{
  id?: string;
  centerX: number;
  centerY: number;
  initWidth: number;
  initHeight: number;
  scaleFactor: number;
  type?: FlowPathType;
  index: number;
  rotate?: number;
  fill?: string;
}>();

let compCommon: CompCommon<FlowPathPanelInfo> | null = null;
let svgInfo: SvgInfo | null = null;
let isEditor = ref(false);
if (props.id) {
  compCommon = inject(CompCommonKey) as CompCommon<FlowPathPanelInfo>;
  svgInfo = compCommon.getSvgInfo() as SvgInfo;
  isEditor = compCommon.isEditor;
}
const component = defineAsyncComponent(() => import(`./resource/${props.type || FlowPathType.Arrow}.vue`));
let _x = props.centerX - props.initWidth / 2;
let _y = props.centerY - props.initHeight / 2;
// eslint-disable-next-line vue/no-setup-props-destructure
let _width = props.initWidth;
// eslint-disable-next-line vue/no-setup-props-destructure
let _height = props.initHeight;
const x = ref(_x * props.scaleFactor);
const y = ref(_y * props.scaleFactor);
const width = ref(_width * props.scaleFactor);
const height = ref(_height * props.scaleFactor);
watch([x, y, width, height], ([v1, v2, v3, v4]) => {
  _x = v1 / props.scaleFactor;
  _y = v2 / props.scaleFactor;
  _width = v3 / props.scaleFactor;
  _height = v4 / props.scaleFactor;
});
watch(
  () => props.centerX,
  (v) => {
    x.value = v - width.value / 2;
  }
);
watch(
  () => props.centerY,
  (v) => {
    y.value = v - height.value / 2;
  }
);
watch(
  () => props.rotate,
  (v) => {
    rotate.value = v || 0;
  }
);
watch(
  () => props.scaleFactor,
  (v) => {
    x.value = _x * v;
    y.value = _y * v;
    width.value = _width * v;
    height.value = _height * v;
  }
);

const operCursors = ref<CSSProperties["cursor"][]>([
  "nw-resize",
  "n-resize",
  "ne-resize",
  "w-resize",
  "se-resize",
  "s-resize",
  "sw-resize",
  "w-resize",
]);
const selected = ref(false);
const points = computed<Point[]>(() => {
  return [
    { x: x.value, y: y.value },
    { x: x.value + width.value / 2, y: y.value },
    { x: x.value + width.value, y: y.value },
    { x: x.value + width.value, y: y.value + height.value / 2 },
    { x: x.value + width.value, y: y.value + height.value },
    { x: x.value + width.value / 2, y: y.value + height.value },
    { x: x.value, y: y.value + height.value },
    { x: x.value, y: y.value + height.value / 2 },
  ];
});
onBeforeUnmount(() => {
  if (!compCommon) return;
  compCommon.connection.disConnect(props.id as string);
  if (selected.value) {
    compCommon.toggleSelect({
      type: ConfigurationType.FlowPath,
      selected: false,
      index: props.index,
    });
  }
});

function select(multiple) {
  if (!compCommon) return;
  selected.value = true;
  compCommon.toggleSelect({
    id: props.id as string,
    multiple,
    type: ConfigurationType.FlowPath,
    selected: true,
    index: props.index,
    unSelect() {
      selected.value = false;
    },
    getFlowPathInfo() {
      return {
        x: x.value,
        y: y.value,
        width: width.value,
        height: height.value,
        rotate: rotate.value,
        setRotate(val) {
          rotate.value = val;
        },
      };
    },
    drag: {
      start,
      move,
    },
  });
}

/**
 * 拖动整个流程组件
 */
function move({ dx, dy, startData }: DragMoveOptions<Point>) {
  if (startData) {
    x.value = startData.x + (dx as number);
    y.value = startData.y + (dy as number);
  }
}
function start() {
  return {
    x: x.value,
    y: y.value,
  };
}
let mousedown: DragStart<[]> | undefined;
if (props.id) {
  const { dragStart } = useDrag<[], Point & { isDragSelect: boolean }>({
    unStopPropagation: !isEditor.value,
    element: svgInfo ? svgInfo.container : undefined,
    start() {
      const isDragSelect = (compCommon as CompCommon).multipleSelect.startTogether(props.id as string);
      if (!isDragSelect) {
        select(false);
      }
      return {
        x: x.value,
        y: y.value,
        isDragSelect,
      };
    },
    move({ dx, dy, startResult }) {
      if (!isEditor.value) return;
      if (startResult.isDragSelect) {
        (compCommon as CompCommon).multipleSelect.moveTogether({ dx, dy });
      } else {
        x.value = startResult.x + dx;
        y.value = startResult.y + dy;
        (compCommon as CompCommon).connection.moveTogether(props.id as string, {
          connectPoints: getRotateConnectPoints(),
        });
      }
    },
    end({ startResult }) {
      if (isEditor.value) {
        if (startResult.isDragSelect) {
          (compCommon as CompCommon).multipleSelect.endTogether();
        } else {
          (compCommon as CompCommon).connection.endTogether(props.id as string);
        }
      }
    },
  });
  mousedown = dragStart;
}
/**
 * 连接处理
 */
function getRotateConnectPoints() {
  const connetPoints: Point[] = [];
  const originalPoints = points.value;
  const centerPoints = {
    x: originalPoints[1].x,
    y: originalPoints[3].y,
  };
  originalPoints.forEach((point) => {
    connetPoints.push(getRotatePoint(centerPoints, point, rotate.value));
  });
  return connetPoints;
}
function getDragInfo(connectPositionIndex?: number) {
  return {
    id: props.id as string,
    type: ConfigurationType.FlowPath,
    start,
    move: move,
    extra: {
      connectPositionIndex,
    },
  };
}
const { opacity, curConnectPoint, mousemove, mouseout, mouseover, mouseup } = useConnEffect({
  getConnectPoints: getRotateConnectPoints,
  getDraggingRect() {
    return compCommon?.draggingPipePathRect;
  },
  setDragInfo(index) {
    if (compCommon) {
      compCommon.connectingDevice = getDragInfo(index);
    }
  },
});
/**
 * rotate
 */
const { rotate, rotateStart } = useRotate({
  getRelativeElement() {
    return svgInfo ? svgInfo.container : document.body;
  },
  centerPoint() {
    return {
      x: points.value[1].x,
      y: points.value[3].y,
    };
  },
  move() {
    (compCommon as CompCommon).connection.moveTogether(props.id as string, {
      connectPoints: getRotateConnectPoints(),
    });
  },
});
if (props.rotate !== undefined) {
  // eslint-disable-next-line vue/no-setup-props-destructure
  rotate.value = props.rotate;
}

/**
 * 拖动流程组件的顶点
 */
const { rectDragStart } = useRectDrag({
  x,
  y,
  width,
  height,
  element: svgInfo?.container,
  rotate,
  points,
  move() {
    (compCommon as CompCommon).connection.moveTogether(props.id as string, {
      connectPoints: getRotateConnectPoints(),
    });
  },
});
/**
 * contextmenu
 */
function contextmenu(e: MouseEvent, el: SVGGElement) {
  if (!compCommon) return;
  const arr: ContextmenuItem[] = [];
  if (compCommon.connection.isConnect(props.id as string)) {
    arr.push({
      name: "解除连接",
      handler() {
        (compCommon as CompCommon).connection.disConnect(props.id as string);
      },
    });
  }
  compCommon.showContextmenu(e, {
    data: arr,
    id: props.id,
    element: el,
  });
}
/**
 * update panel
 */
if (compCommon) {
  watch([x, y, width, height, rotate], ([x, y, width, height, rotate]) => {
    compCommon?.updatePanelInfo({
      type: ConfigurationType.FlowPath,
      panelInfo: { x, y, width, height, rotate },
    });
  });
}
/**
 * 设置文字
 */
const textValue = ref("");
const textHeight = ref(0);
const textWidth = ref(0);
const textEditable = ref(false);
const textX = computed(() => {
  return x.value + width.value / 2 - textWidth.value / 2;
});
const textY = computed(() => {
  return y.value + height.value / 2 - textHeight.value / 2;
});
function setText() {
  if (!isEditor.value || !props.id) return;
  textEditable.value = true;
}
/**
 * expose
 */
const myExpose: ConfigurationInstance<FlowPathJSON> = {
  isInBoxSelect(boxSelect) {
    const isIn = isRectIntersect(
      { x: x.value, y: y.value },
      width.value,
      height.value,
      { x: boxSelect.x, y: boxSelect.y },
      boxSelect.width,
      boxSelect.height
    );
    return isIn;
  },
  select() {
    select(true);
  },
  toJSON() {
    return {
      id: props.id as string,
      x: _x,
      y: _y,
      width: _width,
      height: _height,
      type: props.type || FlowPathType.Arrow,
      rotate: rotate.value,
      text: textValue.value,
    };
  },
  setJSON(data) {
    rotate.value = data.rotate;
    textValue.value = data.text || "";
  },
  getDragInfo,
  previewCondition() {},
  restoreCondition() {},
  getOriginalProps() {},
  getBox() {
    return {
      x: x.value,
      y: y.value,
      width: width.value,
      height: height.value,
      maxX: x.value + width.value,
      maxY: y.value + height.value,
    };
  },
  translate(dx, dy) {
    x.value = x.value + dx;
    y.value = y.value + dy;
  },
};
defineExpose(myExpose);
</script>
